package lottery

import (
	"fmt"
	"math"
	"math/rand"
	"sort"
	"time"

	"lottery/constant"
	"lottery/models"
	"lottery/utils"

	"github.com/astaxie/beego/logs"
)

type PlayCell struct {
	TriggerPlatformId int // 触发的平台
	Order             bool
	Policy            *models.Policy
	GameAgentId       int64 // 下注账号
	GameData          *models.GameData
	OtherGameAgentId  int64 // 下注账号
	OtherGameData     *models.GameData
	MergeRate         float64
	Tick              bool
}

func (p *PlayCell) GetHPlatformId() int {
	if p.Order {
		return p.GameData.PlatformId
	} else {
		return p.OtherGameData.PlatformId
	}
}

type PlayCells []*PlayCell

func (p PlayCells) Len() int { return len(p) }

func (p PlayCells) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

// 倒序
func (p PlayCells) Less(i, j int) bool { return p[i].MergeRate > p[j].MergeRate }

func (u *UserProc) limitHandicapType(handicapType int) bool {
	return u.handicapTypeMap[handicapType]
}

func (u *UserProc) trigger(gameDatas []*models.GameData) {
	logs.Debug("判断可关注水位 %d", len(gameDatas))

	if len(gameDatas) == 0 {
		return
	}

	var limitOffset int64 = 5 * 1000

	if utils.CurrentMills()-gameDatas[0].CreateTime > limitOffset {
		logs.Debug("平台:%d 数据超时 %d", gameDatas[0].PlatformId, utils.CurrentMills()-gameDatas[0].CreateTime)
		return
	}

	u.mux.RLock()
	policys := u.policys
	u.mux.RUnlock()

	playCells := make([]*PlayCell, 0, len(gameDatas))
	for _, gameData := range gameDatas {
		if !u.limitHandicapType(gameData.HandicapType) {
			logs.Debug("盘口类型限制")
			continue
		}

		now := utils.CurrentMills()
		game, ok := u.getGame(gameData.PlatformId, gameData.PlatformGameId)
		if !ok {
			logs.Debug("无对应比赛信息")
			continue
		}

		offset := now - game.StartTime
		if 40*60*1000 < offset && offset < 45*60*1000 {
			// 上半场结束前5分钟不要
			logs.Debug("上半场最后5分钟不再下注")
			continue
		}

		// 中场休息15分钟
		if 95*60*1000 < offset {
			// 下半场结束前10分钟不要
			logs.Debug("最后10分钟不再下注")
			continue
		}

		var gamePolicy *models.Policy
		for _, policy := range policys {
			if policy.Platform1Id != gameData.PlatformId &&
				policy.Platform2Id != gameData.PlatformId {
				// 平台不符合策略
				continue
			}

			if policy.Platform1GameId != gameData.GameId &&
				policy.Platform2GameId != gameData.GameId {
				// 比赛球赛不符合
				continue
			}

			gamePolicy = policy
		}

		if gamePolicy == nil {
			logs.Debug("无对应策略")
			continue
		}

		if gamePolicy.MinRate > gameData.HRate || gamePolicy.MinRate > gameData.ARate {
			// 最低水位不符合
			logs.Debug("最低水位过滤")
			continue
		}

		if gamePolicy.MaxRate < gameData.HRate || gamePolicy.MaxRate < gameData.ARate {
			// 最高水位不符合
			logs.Debug("最高水位过滤")
			continue
		}

		var otherPlatformId int
		var otherGameId int64

		currentPlatformId := gameData.PlatformId
		if gamePolicy.Platform1Id == currentPlatformId {
			otherPlatformId = gamePolicy.Platform2Id
		} else {
			otherPlatformId = gamePolicy.Platform1Id
		}

		currentGameId := gameData.GameId
		if gamePolicy.Platform1GameId == currentGameId {
			otherGameId = gamePolicy.Platform2GameId
		} else {
			otherGameId = gamePolicy.Platform1GameId
		}

		handicap := gameData.Handicap
		if gameData.HandicapType == 1 || gameData.HandicapType == 3 {
			// 让球盘
			handicap = -handicap
		}

		otherGameData := u.getGameData(otherPlatformId, otherGameId, gameData.HandicapType, handicap)
		if otherGameData == nil {
			// 其它站点无数据
			logs.Debug("%d 平台无%d比赛信息", otherPlatformId, otherGameId)
			continue
		}

		if otherGameData.HScore != gameData.HScore || otherGameData.AScore != gameData.AScore {
			// 比赛得分不一致
			logs.Debug("比赛比分不一致 gameId:%d %d:%d otherGameId:%d %d:%d",
				gameData.GameId, gameData.HScore, gameData.AScore,
				otherGameData.GameId, otherGameData.HScore, otherGameData.AScore,
			)
			continue
		}

		offsetTime := gameData.CreateTime - otherGameData.CreateTime
		if math.Abs(float64(offsetTime)) > float64(limitOffset) {
			// 相差5秒数据失效
			platformId := gameData.PlatformId
			agentId := gameData.AgentId
			if offsetTime < 0 {
				platformId = otherGameData.PlatformId
				agentId = otherGameData.AgentId
			}
			logs.Debug("平台:%d 账号:%d 数据失效 %d ms", platformId, agentId, offsetTime)
			break
		}

		logs.Debug("=============策略判断=============")
		// 下单判断

		// timeout := time.Second * 5
		if u.policyRuleNeal(gameData.HRate, otherGameData.ARate) {
			if (gameData.PlatformId != constant.ISN99 || gameData.HIndicator != 2) &&
				(otherGameData.PlatformId != constant.ISN99 || otherGameData.AIndicator != 2) {
				// isn不下小球
				gameData.PreHRate = gameData.HRate
				otherGameData.PreARate = otherGameData.ARate
				playCells = append(playCells, &PlayCell{
					Order:         true,
					Policy:        gamePolicy,
					GameData:      gameData,
					OtherGameData: otherGameData,
					MergeRate:     gameData.HRate + otherGameData.ARate,
				})
			}

		}

		// ================
		if u.policyRuleNeal(gameData.ARate, otherGameData.HRate) {
			if (gameData.PlatformId != constant.ISN99 || gameData.AIndicator != 2) &&
				(otherGameData.PlatformId != constant.ISN99 || otherGameData.HIndicator != 2) {
				// isn不下小球
				gameData.PreARate = gameData.ARate
				otherGameData.PreHRate = otherGameData.HRate
				playCells = append(playCells, &PlayCell{
					Order:         false,
					Policy:        gamePolicy,
					GameData:      gameData,
					OtherGameData: otherGameData,
					MergeRate:     gameData.ARate + otherGameData.HRate,
				})
			}
		}
	}

	logs.Debug("符合水位共%d组", len(playCells))
	if len(playCells) == 0 {
		return
	}

	// sort
	sort.Sort(PlayCells(playCells))
	if len(playCells) > u.followLimit {
		// 取最大前5
		playCells = playCells[:u.followLimit]
	}

	logs.Debug("进入点水池共%d组", len(playCells))
	u.tickCellChan <- playCells
}

func (u *UserProc) getTickCellKey(platformId int, handicapType int, handicap float64) string {
	return fmt.Sprintf("%d_%d_%0.2f", platformId, handicapType, math.Abs(handicap))
}

// 水位合并点水列表
func (u *UserProc) ticksMerge(tickMap map[string]*PlayCell, tickCells []*PlayCell) map[string]*PlayCell {
	// 清除列表中不存在的水位
	for key, tickCell := range tickMap {
		gameData := tickCell.GameData
		if u.getGameData(gameData.PlatformId, gameData.GameId, gameData.HandicapType, gameData.Handicap) == nil {
			delete(tickMap, key)
		}

		otherGameData := tickCell.OtherGameData
		if u.getGameData(otherGameData.PlatformId, otherGameData.GameId, otherGameData.HandicapType, otherGameData.Handicap) == nil {
			delete(tickMap, key)
		}
	}

	for _, tickCell := range tickCells {
		gameData := tickCell.GameData
		if u.getGameData(gameData.PlatformId, gameData.GameId, gameData.HandicapType, gameData.Handicap) == nil {
			continue
		}

		otherGameData := tickCell.OtherGameData
		if u.getGameData(otherGameData.PlatformId, otherGameData.GameId, otherGameData.HandicapType, otherGameData.Handicap) == nil {
			continue
		}

		key := u.getTickCellKey(tickCell.GetHPlatformId(), tickCell.GameData.HandicapType, tickCell.GameData.Handicap)
		oldTickCell, ok := tickMap[key]
		if !ok || !oldTickCell.Tick {
			tickMap[key] = tickCell
		}
	}

	tickCells = make([]*PlayCell, 0, len(tickMap))
	for _, tickCell := range tickMap {
		tickCells = append(tickCells, tickCell)
	}

	sort.Sort(PlayCells(tickCells))
	if len(tickCells) > u.followLimit {
		// 取最大前5
		tickCells = tickCells[:u.followLimit]
	}

	tickTmpMap := make(map[string]*PlayCell)
	for _, tickCell := range tickCells {
		key := u.getTickCellKey(tickCell.GetHPlatformId(), tickCell.GameData.HandicapType, tickCell.GameData.Handicap)
		tickTmpMap[key] = tickCell
	}

	return tickTmpMap
}

func (u *UserProc) gameDataTick(gameLottery ILottery, gameData *models.GameData, gameDataTeam int) (gameNewRate float64, tickOk bool, discard bool) {
	// 获取账号
	// 点水
	if gameLottery.Platform() == constant.U16888 {
		if !u.fillU16888HandicapId(gameLottery.AgentInfo(), gameData) {
			return
		}
	}

	gameNewRate, gameTickData, discard, err := gameLottery.Tick(gameDataTeam, gameData)
	if err != nil {
		logs.Warn("platformId:%d agentId:%d handicap:%0.2f team:%d 点水失败 err:%s",
			gameData.PlatformId,
			gameLottery.AgentInfo().Id,
			gameData.Handicap,
			gameDataTeam,
			err.Error(),
		)
		return
	}
	gameData.GameTickData = gameTickData
	tickOk = true
	return
}

// 点水更新
func (u *UserProc) tickUpdate(tickChan chan *PlayCell) {
	for tickCell := range tickChan {
		gameData := tickCell.GameData
		otherGameData := tickCell.OtherGameData
		var (
			place            bool
			discardCell      bool
			currentTick      bool // 当次点水
			gameRate         float64
			otherGameRate    float64
			gameNewRate      float64
			otherGameNewRate float64
			gameOffset       int64
			otherGameOffset  int64
			// limit            int64   = 1200
			rowMergeRate float64 = tickCell.MergeRate
		)

		if tickCell.Order {
			gameRate = tickCell.GameData.HRate
			gameNewRate = gameRate
			otherGameRate = tickCell.OtherGameData.ARate
			otherGameNewRate = otherGameRate
			now := utils.CurrentMills()
			gameOffset = now - gameData.CreateTime
			otherGameOffset = now - otherGameData.CreateTime

			if otherGameData.PlatformId == tickCell.TriggerPlatformId {
				otherGameLottery, ok := u.getPlaceLottery(otherGameData.GetAgentId(), otherGameData.PlatformId)
				if !ok {
					logs.Warn("点水 platformId:%d agentId:%d 获取不到账号",
						otherGameData.PlatformId,
						otherGameData.GetAgentId(),
					)
				} else {
					rate, tickOk, discard := u.gameDataTick(otherGameLottery, otherGameData, 2)
					if discard {
						discardCell = discard
					} else if tickOk {
						currentTick = true
						otherGameNewRate = rate
						now := utils.CurrentMills()
						if int(otherGameData.ARate*100) != int(otherGameNewRate*100) {
							otherGameData.HadUpdate = true
							otherGameData.PreARate = otherGameData.ARate
							otherGameData.ARate = otherGameNewRate
							if otherGameData.UpdateTime > 0 {
								otherGameData.CreateTime = otherGameData.UpdateTime
							}
							otherGameData.GameTickData = nil
							otherGameOffset = now - otherGameData.UpdateTime
						} else {
							otherGameOffset = now - otherGameData.CreateTime
						}

						gameOffset = now - gameData.CreateTime
						otherGameData.UpdateTime = now

						if u.policyRule(gameNewRate, otherGameNewRate) {
							place = true
							otherGameData.TriggerTime = now
							tickCell.OtherGameAgentId = otherGameLottery.AgentInfo().Id

							// 触发先下
							gameRate, otherGameRate = otherGameRate, gameRate
							gameNewRate, otherGameNewRate = otherGameNewRate, gameNewRate
							gameOffset, otherGameOffset = otherGameOffset, gameOffset
							tickCell.Order = tickCell.Order != true
							tickCell.GameData, tickCell.OtherGameData = tickCell.OtherGameData, tickCell.GameData
							tickCell.GameAgentId, tickCell.OtherGameAgentId = tickCell.OtherGameAgentId, tickCell.GameAgentId
						}
					}

					if place {
						u.putPlaceLotteryWithPrepare(otherGameLottery, true)
					} else {
						u.putPlaceLotteryWithPrepare(otherGameLottery, false)
					}
				}
			} else {
				gameLottery, ok := u.getPlaceLottery(gameData.GetAgentId(), gameData.PlatformId)
				if !ok {
					logs.Warn("点水 platformId:%d agentId:%d 获取不到账号",
						gameData.PlatformId,
						gameData.GetAgentId(),
					)
				} else {
					rate, tickOk, discard := u.gameDataTick(gameLottery, gameData, 1)
					if discard {
						discardCell = discard
					} else if tickOk {
						currentTick = true
						gameNewRate = rate
						now := utils.CurrentMills()
						if int(gameData.HRate*100) != int(gameNewRate*100) {
							gameData.HadUpdate = true
							gameData.PreHRate = gameData.HRate
							gameData.HRate = gameNewRate
							if gameData.UpdateTime > 0 {
								gameData.CreateTime = gameData.UpdateTime
							}
							gameData.GameTickData = nil
							gameOffset = now - gameData.UpdateTime
						} else {
							gameOffset = now - gameData.CreateTime
						}

						otherGameOffset = now - otherGameData.CreateTime
						gameData.UpdateTime = now

						if u.policyRule(gameNewRate, otherGameNewRate) {
							place = true
							gameData.TriggerTime = now
						}
					}

					if place {
						u.putPlaceLotteryWithPrepare(gameLottery, true)
					} else {
						u.putPlaceLotteryWithPrepare(gameLottery, false)
					}
				}
			}
		} else {
			gameRate = tickCell.GameData.ARate
			gameNewRate = gameRate
			otherGameRate = tickCell.OtherGameData.HRate
			otherGameNewRate = otherGameRate
			now := utils.CurrentMills()
			gameOffset = now - gameData.CreateTime
			otherGameOffset = now - otherGameData.CreateTime

			if otherGameData.PlatformId == tickCell.TriggerPlatformId {
				otherGameLottery, ok := u.getPlaceLottery(otherGameData.GetAgentId(), otherGameData.PlatformId)
				if !ok {
					logs.Warn("点水 platformId:%d agentId:%d 获取不到账号",
						otherGameData.PlatformId,
						otherGameData.GetAgentId(),
					)
				} else {
					rate, tickOk, discard := u.gameDataTick(otherGameLottery, otherGameData, 1)
					if discard {
						discardCell = discard
					} else if tickOk {
						currentTick = true
						otherGameNewRate = rate
						now := utils.CurrentMills()
						if int(otherGameData.HRate*100) != int(otherGameNewRate*100) {
							otherGameData.HadUpdate = true
							otherGameData.PreHRate = otherGameData.HRate
							otherGameData.HRate = otherGameNewRate
							if otherGameData.UpdateTime > 0 {
								otherGameData.CreateTime = otherGameData.UpdateTime
							}
							otherGameData.GameTickData = nil
							otherGameOffset = now - otherGameData.UpdateTime
						} else {
							otherGameOffset = now - otherGameData.CreateTime
						}

						gameOffset = now - gameData.CreateTime
						otherGameData.UpdateTime = now

						if u.policyRule(gameNewRate, otherGameNewRate) {
							place = true
							otherGameData.TriggerTime = now
							tickCell.OtherGameAgentId = otherGameLottery.AgentInfo().Id

							// 触发先下
							gameRate, otherGameRate = otherGameRate, gameRate
							gameNewRate, otherGameNewRate = otherGameNewRate, gameNewRate
							gameOffset, otherGameOffset = otherGameOffset, gameOffset
							tickCell.Order = tickCell.Order != true
							tickCell.GameData, tickCell.OtherGameData = tickCell.OtherGameData, tickCell.GameData
							tickCell.GameAgentId, tickCell.OtherGameAgentId = tickCell.OtherGameAgentId, tickCell.GameAgentId
						}
					}

					if place {
						u.putPlaceLotteryWithPrepare(otherGameLottery, true)
					} else {
						u.putPlaceLotteryWithPrepare(otherGameLottery, false)
					}
				}
			} else {
				gameLottery, ok := u.getPlaceLottery(gameData.GetAgentId(), gameData.PlatformId)
				if !ok {
					logs.Warn("点水 platformId:%d agentId:%d 获取不到账号",
						gameData.PlatformId,
						gameData.GetAgentId(),
					)
				} else {
					rate, tickOk, discard := u.gameDataTick(gameLottery, gameData, 2)
					if discard {
						discardCell = discard
					} else if tickOk {
						currentTick = true
						gameNewRate = rate
						now := utils.CurrentMills()
						if int(gameData.ARate*100) != int(gameNewRate*100) {
							gameData.HadUpdate = true
							gameData.PreARate = gameData.ARate
							gameData.ARate = gameNewRate
							if gameData.UpdateTime > 0 {
								gameData.CreateTime = gameData.UpdateTime
							}
							gameData.GameTickData = nil
							gameOffset = now - gameData.UpdateTime
						} else {
							gameOffset = now - gameData.CreateTime
						}

						otherGameOffset = now - otherGameData.CreateTime
						gameData.UpdateTime = now

						if u.policyRule(gameNewRate, otherGameNewRate) {
							place = true
							gameData.TriggerTime = now
							tickCell.GameAgentId = gameLottery.AgentInfo().Id
						}
					}

					if place {
						u.putPlaceLotteryWithPrepare(gameLottery, true)
					} else {
						u.putPlaceLotteryWithPrepare(gameLottery, false)
					}
				}
			}
		}

		// 再次过滤
		tickCell.MergeRate = gameNewRate + otherGameNewRate
		msg := fmt.Sprintf("policy:%d 点水站点:%d 盘口类型:%d 盘口大小:%0.2f "+
			"点水前 rate1:%0.2f rate2:%0.2f 合水:%0.2f "+
			"点水后 rate1:%0.2f rate2:%0.2f 合水:%0.2f "+
			"水位存在时长1:%d 时长2:%d 单次点水成功:%v 后下点过水:%v",
			tickCell.Policy.Id, tickCell.TriggerPlatformId, tickCell.GameData.HandicapType, tickCell.GameData.Handicap,
			gameRate, otherGameRate, rowMergeRate,
			gameNewRate, otherGameNewRate, tickCell.MergeRate,
			gameOffset, otherGameOffset, currentTick, tickCell.OtherGameData.HadUpdate)

		if discardCell ||
			!currentTick ||
			// otherGameOffset > limit ||
			!u.policyRule(gameNewRate, otherGameNewRate) {
			// 释放资源
			place = false
			if tickCell.GameAgentId > 0 {
				u.setLotterySleep(tickCell.GameAgentId, tickCell.GameData.PlatformId)
				tickCell.GameAgentId = 0
			}

			if tickCell.OtherGameAgentId > 0 {
				u.setLotterySleep(tickCell.OtherGameAgentId, tickCell.OtherGameData.PlatformId)
				tickCell.OtherGameAgentId = 0
			}

			// 放弃该组
			if discardCell {
				// 丢弃
				return
			}

			tickCell.GameData.GameTickData = nil
			tickCell.OtherGameData.GameTickData = nil
		}

		if place {
			logs.Notice("%s 可下单", msg)
			u.playCellChan <- []*PlayCell{tickCell}
		} else {
			logs.Warn("%s 超时或者合水条件不符合", msg)
			if !discardCell && u.policyRuleNeal(gameNewRate, otherGameNewRate) {
				u.tickCellChan <- []*PlayCell{tickCell}
			}
		}
	}
}

// 点水层，点水关注列表中的水位
func (u *UserProc) tickProc() {
	logs.Debug("%+v", u.resCfg)
	ps3838TickTime := time.NewTicker(time.Millisecond * time.Duration(u.resCfg.PS3838Tick))
	isnTickTime := time.NewTicker(time.Millisecond * time.Duration(u.resCfg.ISNTick))
	u16888TickTime := time.NewTicker(time.Millisecond * time.Duration(u.resCfg.U16888Tick))
	chanel := 20
	tickMap := make(map[string]*PlayCell)
	tickChan := make(chan *PlayCell, chanel)
	logs.Notice("账号 %d 共启动个%d点水线程", u.user.Id, chanel)
	for i := 0; i < chanel; i++ {
		go u.tickUpdate(tickChan)
	}

	for {
		select {
		case <-ps3838TickTime.C:
			count := 0
			for key, tickCell := range tickMap {
				if tickCell.GameData.PlatformId != constant.PS3838 &&
					tickCell.OtherGameData.PlatformId != constant.PS3838 {
					continue
				}

				count++
				tickCell.TriggerPlatformId = constant.PS3838
				tickChan <- tickCell
				delete(tickMap, key)
			}
			logs.Notice("PS3838点水更新,共 %d 组", count)
		case <-isnTickTime.C:
			count := 0
			for key, tickCell := range tickMap {
				if tickCell.GameData.PlatformId != constant.ISN99 &&
					tickCell.OtherGameData.PlatformId != constant.ISN99 {
					continue
				}

				count++
				tickCell.TriggerPlatformId = constant.ISN99
				tickChan <- tickCell
				delete(tickMap, key)
			}
			logs.Notice("ISN点水更新,共 %d 组", count)
		case <-u16888TickTime.C:
			count := 0
			for key, tickCell := range tickMap {
				if tickCell.GameData.PlatformId != constant.U16888 &&
					tickCell.OtherGameData.PlatformId != constant.U16888 {
					continue
				}

				count++
				tickCell.TriggerPlatformId = constant.U16888
				tickChan <- tickCell
				delete(tickMap, key)
			}
			logs.Notice("利记点水更新,共 %d 组", count)
		case tickCells := <-u.tickCellChan:
			logs.Debug("收到水位 %d", len(tickChan))
			tickMap = u.ticksMerge(tickMap, tickCells)
		}
	}

}

// 关注合水需调整
func (u *UserProc) policyRuleNeal(rate1, rate2 float64) bool {
	if rand.Intn(1000) < 100 {
		// return true
	}

	offset := -0.1

	if rate1 < 0 && rate2 > 0 && (rate1+rate2) >= offset {
		return true
	}

	if rate1 > 0 && rate2 < 0 && (rate1+rate2) >= offset {
		return true
	}

	if rate1 > 1 && rate2 > 1 {
		// return true
	}

	if rate1 < 0 && rate2 < 0 {
		// return true
	}
	return false
}

func (u *UserProc) policyRule(rate1, rate2 float64) bool {
	if rand.Intn(1000) < 100 {
		// return true
	}

	// 角球临时处理
	if rate1+rate2 > 0.06 {
		// logs.Notice("出现角球数据........ rate1:%0.2f rate2:%0.2f", rate1, rate2)
		return false
	}

	if (rate1 > 0 && (rate1 < 0.8 || rate1 > 1)) ||
		(rate1 < 0 && (rate1 > -0.8 || rate1 < -1)) {
		return false
	}

	if (rate2 > 0 && (rate2 < 0.8 || rate2 > 1)) ||
		(rate2 < 0 && (rate2 > -0.8 || rate2 < -1)) {
		return false
	}

	if rate1 < 0 && rate2 > 0 && (rate1+rate2) >= 0 {
		return true
	}

	if rate1 > 0 && rate2 < 0 && (rate1+rate2) >= 0 {
		return true
	}

	if rate1 > 1 && rate2 > 1 {
		// return true
	}

	if rate1 < 0 && rate2 < 0 {
		// return true
	}
	return false
}

func (u *UserProc) sayHello(gameData, otherGameData *models.GameData) {
	logs.Debug("currenct %+v", gameData)
	logs.Debug("other %+v", otherGameData)
	logs.Debug("==========play order")
	// cmd := exec.Command("say", "hello")
	// cmd.Run()
}
